/*
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gradle.integtests

import org.gradle.foundation.ProjectView
import org.gradle.foundation.TaskView
import org.gradle.foundation.TestUtility
import org.gradle.gradleplugin.foundation.GradlePluginLord
import org.gradle.integtests.fixtures.AbstractIntegrationTest
import org.gradle.integtests.fixtures.Sample
import org.gradle.openapi.external.foundation.ProjectVersion1
import org.gradle.openapi.wrappers.foundation.GradleInterfaceWrapperVersion1
import org.gradle.testfixtures.internal.NativeServicesTestFixture
import org.junit.Assert
import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test

import java.util.concurrent.TimeUnit

import static org.gradle.util.TestPrecondition.NOT_JDK_IBM

/**
 This tests the multiproject sample with the GradleView mechanism.
 */
class MultiprojectProjectAndTaskListIntegrationTest extends AbstractIntegrationTest {

    @Rule
    public final Sample sample = new Sample(testDirectoryProvider, 'java/multiproject')
    GradlePluginLord gradlePluginLord = new GradlePluginLord()

    @Before
    void setUp() {
        // Tests are flaky on IBM JDK
        Assume.assumeTrue(NOT_JDK_IBM.fulfilled);

        NativeServicesTestFixture.initialize()
        gradlePluginLord.setCurrentDirectory(sample.dir);
        gradlePluginLord.setGradleHomeDirectory(distribution.gradleHomeDir);
        gradlePluginLord.addCommandLineArgumentAlteringListener(new ExtraTestCommandLineOptionsListener(executer.gradleUserHomeDir))
    }

    /*
       This tests against the multiproject sample. It expects to find not just
       the root level projects, but also the nested sub projects
       (services:webservice). This isn't really interested in the actual tasks
       themselves (I fear those may change too often to worry with keeping the
       test up to date).
    */

    @Test
    public void multiProjectjavaProjectSample() {

        List<ProjectView> projects = refreshAndGetProjects()

        //make sure there weren't other root projects found.
        Assert.assertEquals(1, projects.size());

        ProjectView rootProject = projects.get(0);
        Assert.assertNotNull(rootProject);
        Assert.assertEquals("multiproject", rootProject.getName());

        //now check for sub projects, api, shared, and services
        ProjectView apiProject = rootProject.getSubProject("api");
        Assert.assertNotNull(apiProject);
        Assert.assertTrue(apiProject.getSubProjects().isEmpty());  //this has no sub projects

        ProjectView sharedProject = rootProject.getSubProject("shared");
        Assert.assertNotNull(sharedProject);
        Assert.assertTrue(sharedProject.getSubProjects().isEmpty());  //this has no sub projects

        ProjectView servicesProject = rootProject.getSubProject("services");
        Assert.assertNotNull(servicesProject);

        //services has a sub project
        ProjectView webservicesProject = servicesProject.getSubProject("webservice");
        Assert.assertNotNull(webservicesProject);
        Assert.assertTrue(webservicesProject.getSubProjects().isEmpty());  //this has no sub projects

        //make sure we didn't inadvertantly find other sub projects.
        Assert.assertEquals(3, rootProject.getSubProjects().size());
    }

    /**
     This tests that the wrappers for projects and tasks are working
     */
    @Test
    public void testOpenAPIWrapperProjectAndTaskList() {

        GradleInterfaceWrapperVersion1 wrapper = new GradleInterfaceWrapperVersion1(gradlePluginLord);

        //the rest of this uses the open API mechanism to access the projects and tasks
        refreshProjectsAndWait()

        //get the root project
        List<ProjectVersion1> projects = wrapper.getRootProjects();
        Assert.assertNotNull(projects);

        //make sure there weren't other root projects found.
        Assert.assertEquals(1, projects.size());

        ProjectVersion1 rootProject = projects.get(0);
        Assert.assertNotNull(rootProject);
        Assert.assertEquals("multiproject", rootProject.getName());

        //now check for sub projects, api, shared, and services
        ProjectVersion1 apiProject = rootProject.getSubProject("api");
        Assert.assertNotNull(apiProject);
        Assert.assertTrue(apiProject.getSubProjects().isEmpty());  //this has no sub projects

        ProjectVersion1 sharedProject = rootProject.getSubProject("shared");
        Assert.assertNotNull(sharedProject);
        Assert.assertTrue(sharedProject.getSubProjects().isEmpty());  //this has no sub projects

        ProjectVersion1 servicesProject = rootProject.getSubProject("services");
        Assert.assertNotNull(servicesProject);

        //services has a sub project
        ProjectVersion1 webservicesProject = servicesProject.getSubProject("webservice");
        Assert.assertNotNull(webservicesProject);
        Assert.assertTrue(webservicesProject.getSubProjects().isEmpty());  //this has no sub projects

        //make sure we didn't inadvertantly find other sub projects.
        Assert.assertEquals(3, rootProject.getSubProjects().size());

        //I don't want to keep the actual tasks in synch, but let's make sure there's something there.
        def tasks = apiProject.getTasks()
        Assert.assertNotNull(tasks);
        Assert.assertFalse(tasks.isEmpty());
    }

    /**
     * This tests ProjectView.getSubProjectFromFullPath. Specifically, the first character
     * is optionally a colon. So this tests it both ways.
     */
    @Test
    public void testSubProjectFromFullPath() {

        List<ProjectView> projects = refreshAndGetProjects()
        Assert.assertFalse(projects.isEmpty());

        ProjectView rootProject = projects.get(0)

        //test it using no prefixed colon
        ProjectView foundProject1 = rootProject.getSubProjectFromFullPath("services:webservice")
        Assert.assertNotNull(foundProject1)

        //test it using a prefixed colon
        ProjectView foundProject2 = rootProject.getSubProjectFromFullPath(":services:webservice")
        Assert.assertNotNull(foundProject2)

        //should both the same project
        Assert.assertEquals(foundProject1, foundProject2)
    }

    /**
     * This tests TaskView.getTaskFromFullPath. Specifically, the first character
     * is optionally a colon. So this tests it both ways.
     */
    @Test
    public void testGetTaskFromFullPath() {

        List<ProjectView> projects = refreshAndGetProjects()
        Assert.assertFalse(projects.isEmpty());

        ProjectView rootProject = projects.get(0)

        //test it using no prefixed colon
        TaskView foundTask1 = rootProject.getTaskFromFullPath("api:build")
        Assert.assertNotNull(foundTask1)

        //test it using a prefixed colon
        TaskView foundTask2 = rootProject.getTaskFromFullPath(":api:build")
        Assert.assertNotNull(foundTask2)

        //should both the same project
        Assert.assertEquals(foundTask1, foundTask2)
    }

    private List<ProjectView> refreshAndGetProjects() {
        refreshProjectsAndWait()

        List<ProjectView> projects = gradlePluginLord.getProjects()
        Assert.assertNotNull(projects)
        projects
    }

    /**
     * Refresh the projects and wait. This will throw an exception if it fails.
     */
    private refreshProjectsAndWait() {
        TestUtility.refreshProjectsBlocking(gradlePluginLord, 100, TimeUnit.SECONDS)
    }
}
